package be.kuleuven.cs.mop.domain.model.impl;


import java.util.ArrayList;
import java.util.List;

import be.kuleuven.cs.mop.domain.exceptions.TaskManagerException;
import be.kuleuven.cs.mop.domain.model.Invitation;
import be.kuleuven.cs.mop.domain.model.InvitationManager;
import be.kuleuven.cs.mop.domain.model.Task;
import be.kuleuven.cs.mop.domain.model.User;
import be.kuleuven.cs.mop.domain.model.UserType;



/**
 * Internal implementation of the {@link InvitationManager} interface
 */
public class InvitationManagerImpl implements InvitationManager {
	
	/**
	 * The list of invitations stored by this <code>InvitationManager</code>
	 */
	private List<InvitationImpl> invitations = new ArrayList<InvitationImpl>();
	
	
	
	@Override
	public void accept(final InvitationImpl invitation, final UserImpl user)
			throws TaskManagerException {
		if (!invitation.getUser().equals(user))
			throw new TaskManagerException(
					TaskManagerException.NOT_ALLOWED_TO_MODIFY_INVITATION);
		
		invitation.setAccepted();
	}
	
	/**
	 * Removes the specified {@link Invitation} from the internal list
	 * 
	 * @param invitation - an {@link InvitationImpl}
	 */
	private void add(final InvitationImpl invitation) {
		invitations.add(invitation);
	}
	
	@Override
	public List<InvitationImpl> getInvitations() {
		return new ArrayList<InvitationImpl>(invitations);
	}
	
	@Override
	public List<InvitationImpl> getInvitations(final Task task) {
		final List<InvitationImpl> invitations = new ArrayList<InvitationImpl>();
		
		for (final InvitationImpl invitation : getInvitations())
			if (invitation.getTask().equals(task))
				invitations.add(invitation);
		
		return invitations;
	}
	
	@Override
	public List<InvitationImpl> getInvitations(final Task task, UserType type) {
		final List<InvitationImpl> invitations = new ArrayList<InvitationImpl>();
		
		for (final InvitationImpl invitation : getInvitations())
			if (invitation.getTask().equals(task) && invitation.getUser().getType().equals(type))
				invitations.add(invitation);
		
		return invitations;
	}
	
	
	
	
	
	@Override
	public List<InvitationImpl> getInvitations(final User user) {
		final List<InvitationImpl> invitations = new ArrayList<InvitationImpl>();
		
		for (final InvitationImpl invitation : getInvitations())
			if (invitation.getUser().equals(user))
				invitations.add(invitation);
		
		return invitations;
	}
	
	@Override
	public List<TaskImpl> getInvitedTasks(final User user) {
		final List<TaskImpl> tasks = new ArrayList<TaskImpl>();
		
		for (final Invitation invitation : getInvitations(user))
			tasks.add((TaskImpl)invitation.getTask());
		
		return tasks;
	}
	
	@Override
	public List<InvitationImpl> getInvitationsAccepted(final Task task) {
		final List<InvitationImpl> invitations = new ArrayList<InvitationImpl>();
		
		for (final InvitationImpl invitation : getInvitations(task))
			if (invitation.isAccepted())
				invitations.add(invitation);
		
		return invitations;
	}
	
	@Override
	public List<InvitationImpl> getInvitationsAccepted(final User user) {
		final List<InvitationImpl> invitations = new ArrayList<InvitationImpl>();
		
		for (final InvitationImpl invitation : getInvitations())
			if (invitation.getUser().equals(user)
					&& !invitation.getTask().getUser().equals(user)
					&& invitation.isAccepted())
				invitations.add(invitation);
		
		return invitations;
	}
	
	@Override
	public List<InvitationImpl> getInvitationsPending(final User user) {
		final List<InvitationImpl> invitations = new ArrayList<InvitationImpl>();
		
		for (final InvitationImpl invitation : getInvitations())
			if (invitation.getUser().equals(user)
					&& !invitation.getTask().getUser().equals(user)
					&& invitation.isPending())
				invitations.add(invitation);
		
		return invitations;
	}
	
	@Override
	public void invite(final UserImpl user, final TaskImpl task)
			throws TaskManagerException {
		//	Determine whether or not the user is the owner of the task
		if (task.getUser().equals(user))
			throw new TaskManagerException(
					TaskManagerException.TASK_OWNER_INVITATION_ERROR);
		
		int counter = 1; // starting from 1 to account for the new invitation
		
		//	Determine whether or not the user has already been invited
		for (final InvitationImpl invitation : getInvitations(task)) {
			if (invitation.getUser().equals(user))
				throw new TaskManagerException(
						TaskManagerException.USER_ALREADY_INVITED);
			
			switch (invitation.getStatus()) {
				case ACCEPTED:
					if (invitation.getUser().getType().equals(user.getType()))
						counter++;
					break;
				case PENDING:
					if (invitation.getUser().getType().equals(user.getType()))
						counter++;
					break;
				default:
					//	ignored
			}
		}
		
		//	Determine whether or not the specific user-type is allowed
		//	and its constraints are not violated
		final Interval constraints = task.getInvitationConstraints(user.getType());
		if ((constraints == null) || !constraints.satisfiesUpperLimit(counter))
			throw new TaskManagerException(
					TaskManagerException.INVITATION_CONSTRAINTS_VIOLATED);
		
		add(new InvitationImpl(task, user));
	}
	
	
	
	@Override
	public void reject(final InvitationImpl invitation, final UserImpl user)
			throws TaskManagerException {
		if (!invitation.getUser().equals(user))
			throw new TaskManagerException(
					TaskManagerException.NOT_ALLOWED_TO_MODIFY_INVITATION);
		
		invitation.setRejected();
	}
	
	@Override
	public void remove(final InvitationImpl invitation) {
		invitations.remove(invitation);
	}
	
	@Override
	public void removeAllInvitationsOfTask(final Task task){
		for(InvitationImpl invit : getInvitations(task)){
			remove(invit);
		}
	}
	
	@Override
	public void resetAcceptedInvitations(final Task task) {
		for (final InvitationImpl invitation : getInvitations())
			if (invitation.getTask().equals(task) && invitation.isAccepted())
				invitation.setPending();
	}
	
	
	
	
}